package com.realestate.controller;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import com.realestate.constants.AppConstant;
import com.realestate.constants.RecordStatus;
import com.realestate.dao.ProductCommonAttributeValueDao;
import com.realestate.model.BrtbAttribute;
import com.realestate.model.BrtbBuilding;
import com.realestate.model.BrtbCategory;
import com.realestate.model.BrtbCommonAttribute;
import com.realestate.model.BrtbDistrict;
import com.realestate.model.BrtbInterior;
import com.realestate.model.BrtbProduct;
import com.realestate.model.BrtbProductAttributeValue;
import com.realestate.model.BrtbProductCommonAttrValue;
import com.realestate.model.BrtbProvince;
import com.realestate.model.ProductAttributeValueDTO;
import com.realestate.model.ProductCommonAttributeValueDTO;
import com.realestate.services.AttributeService;
import com.realestate.services.BuildingService;
import com.realestate.services.CategoryService;
import com.realestate.services.CommonAttributeService;
import com.realestate.services.DistrictService;
import com.realestate.services.InteriorService;
import com.realestate.services.ProductAttributeValueService;
import com.realestate.services.ProductCommonAttributeValueService;
import com.realestate.services.ProductService;
import com.realestate.services.ProvinceService;

@Controller
@RequestMapping("/admin/product")
public class ProductController {
	
	private static final Logger logger = Logger.getLogger(ProductController.class);
	
	@Autowired
	private ProvinceService provinceService;
	
	@Autowired
	private DistrictService districtService;
	
	@Autowired
	private CategoryService categoryService;
	
	@Autowired
	private ProductService productService;
	
	@Autowired
	private CommonAttributeService commonAttributeService;
	
	@Autowired
	private ProductCommonAttributeValueService productCommonAttributeService;
	
	@Autowired
	private AttributeService attributeService;
	
	@Autowired
	private ProductAttributeValueService productAttributeValueService;
	
	@Autowired
	private ProductCommonAttributeValueDao productCommonAttributeValueDao;
	
	@Autowired
	private InteriorService interiorService;
	
	@Autowired
	private BuildingService buildingService;
	/**
	 * Product List
	 * @param request
	 * @return 
	 */
	@RequestMapping(value = "/pages", method = RequestMethod.GET)
	public ModelAndView listProducts(HttpServletRequest request){
		int page = 1;
		if(request.getParameter("page") != null){
			page = Integer.parseInt(request.getParameter("page"));
		}
	    Pageable pageable = new PageRequest(page-1, AppConstant.PAGE_SIZE);
	    ModelAndView modelAndView = new ModelAndView("admin/listProducts");
	    Page<BrtbProduct> products = productService.getAllProduct(pageable);
	    modelAndView.addObject("products", products);
	    
	    int current = products.getNumber() + 1;

	    int begin = Math.max(1, current - 5);
	    int end = Math.min(begin + 10, products.getTotalPages());
	    modelAndView.addObject("beginIndex", begin);
	    modelAndView.addObject("endIndex", end);
	    modelAndView.addObject("currentIndex", current);
    	return modelAndView;
	}
	
	/**
	 * create Product POST method
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "/create", method = RequestMethod.POST)
	public @ResponseBody String createProduct(HttpServletRequest request){
		Map<String, String[]> multiParameters = request.getParameterMap();
		// merge product and attribute;
		BrtbProduct product = createProduct(multiParameters);
		
		return product.getId() + ";" + product.getProductName();
	}
	/**
	 * create new product
	 * @return ModelAndView
	 */
	@RequestMapping(value = "/create", method = RequestMethod.GET)
	public ModelAndView getdata() {
		List<BrtbProvince> provinces = provinceService.getAllProvinces();
		List<BrtbCategory> categories = categoryService.selectAll();
		List<BrtbAttribute> attributes = attributeService.getAllAttribute();
		List<BrtbCommonAttribute> commonAttributes = commonAttributeService.getAllCommonAttributes();
		List<BrtbInterior> interiors = initListInterior();
		List<BrtbBuilding> buildings = buildingService.getAllBuilding();
		
		ModelAndView model = new ModelAndView("product");
		if(provinces != null){
			model.addObject("provinces", provinces);
		}
		model.addObject("categories", categories);
		model.addObject("interiors", initListInterior());
		movePictureAttributeToLastItem(attributes);
		model.addObject("attributes", attributes);
		model.addObject("commonAttributes", commonAttributes);
		model.addObject("interiors", interiors);
		model.addObject("buildings", buildings);
		model.addObject("action", AppConstant.CREATE + " " + AppConstant.PRODUCT);
		return model;
	}

	@RequestMapping(value = "/uploadImages", method = RequestMethod.GET)
	public ModelAndView uploadImages(@RequestParam(value="productId") String productId) {
		Long id = Long.parseLong(productId);
		BrtbProduct product = productService.findProductById(id);		
		ModelAndView model = new ModelAndView("productUploadImages");
		if(product != null){
			model.addObject("product", product);
			// delete for => edit image only
			String picture = product.getPicture();
			if(picture != null && picture.length() > 0){
				String rootPath = System.getProperty("catalina.home");
				String[] pictures = picture.split(AppConstant.SEPERATOR_FILE_PICTURE);
				if(pictures != null && pictures.length > 0){
					for(String value : pictures){
						File dir = new File(rootPath + File.separator + AppConstant.APP_URL + File.separator + value);
						if (dir.exists()) {
				            dir.delete();
				        }
	
					}
				}
			}
			productService.resetPictureProduct(id);
		}
		return model;
	}
	
	@RequestMapping(value = "/uploadImages", method = RequestMethod.POST)
	public String uploadImagesPost(HttpServletRequest request) {
		String productId = request.getParameter("productId");
		MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
		if(productId != null){
			try {
				Map<String, MultipartFile> mapFiles = multipartRequest.getFileMap();
				Set<String> setStrings = mapFiles.keySet();
				String rootPath = System.getProperty("catalina.home");
				File dir = new File(rootPath + File.separator + AppConstant.APP_URL + File.separator + AppConstant.PRODUCT);
				if(!dir.exists()){
					dir.mkdirs();
				}
				for(String set: setStrings){
					MultipartFile multipartField = mapFiles.get(set);
					File serverFile = new File(dir.getAbsolutePath() + File.separator + multipartField.getOriginalFilename());
					BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(serverFile));
					stream.write(multipartField.getBytes());
					stream.close();
					logger.info("Server File Location=" + serverFile.getAbsolutePath());
					productService.updatePictureProduct(Long.parseLong(productId), "/" + AppConstant.PRODUCT + "/" + multipartField.getOriginalFilename() + AppConstant.SEPERATOR_FILE_PICTURE);
				}
				return AppConstant.SUCCESS;
			} catch (Exception e) {
				return AppConstant.FAIL;
			}
		}
		return AppConstant.FAIL;
	}
	
	@RequestMapping(value="/getDistrictByProvinceCode", method = RequestMethod.GET)
	public @ResponseBody String getDistrictByProvinceCode(@RequestParam(value = "provinceCode") String provinceCode) { 
		List<BrtbDistrict> districts = districtService.getDistrictByProvinceCode(provinceCode);
		StringBuffer resultString = new StringBuffer();
		if(districts != null && districts.size() > 0){
			for (BrtbDistrict brtbDistrict : districts) {
				resultString.append("<option value=\"");
				resultString.append(brtbDistrict.getDistrictCode());
				resultString.append("\">");
				resultString.append(brtbDistrict.getDistrictName());
				resultString.append("</option>");
			}
		}
		return resultString.toString();  
	}
	
	// remove picture attribute, picture upload after that.
	private void movePictureAttributeToLastItem(List<BrtbAttribute> attributes) {
		BrtbAttribute pictureAttribute = null;
		BrtbAttribute specialAttribute = null;
		
		for (BrtbAttribute brtbAttribute : attributes) {
			if(brtbAttribute.getAttributeName().equalsIgnoreCase(AppConstant.PICTURE)){
				pictureAttribute = brtbAttribute;
			}else if(brtbAttribute.getAttributeName().equalsIgnoreCase(AppConstant.SPECIAL)){
				specialAttribute = brtbAttribute;
			}
		}
		
		if(pictureAttribute != null){
			attributes.remove(pictureAttribute);
		}
		
		if(specialAttribute != null){
			attributes.remove(specialAttribute);
			attributes.add(specialAttribute);
		}
	}
	
	/**
	 * Delete but change record status to C
	 * @param productId
	 * @return
	 */
	@RequestMapping(value = "/delete/{productId}", method = RequestMethod.POST)
	public String closeProducts(@PathVariable Long productId){
		productService.changeRecordStatus(productId, RecordStatus.C);
    	return AppConstant.SUCCESS;
	}
	
	// save edit product
	@RequestMapping(value = "/edit", method = RequestMethod.POST)
	public @ResponseBody String saveEditProduct(HttpServletRequest request){
		Map<String, String[]> multiParameters = request.getParameterMap();
		// merge product and attribute;
		if(multiParameters.get("id")[0] != null){
			BrtbProduct persist = productService.findProductById(Long.parseLong(multiParameters.get("id")[0]));
			BrtbProduct product = productService.mergeProduct(setValueForProduct(persist, multiParameters));
			deleteAllAttributeProduct(Long.parseLong(multiParameters.get(AppConstant.ID)[0]));
			deleteAllCommonAttributeValueProduct(Long.parseLong(multiParameters.get(AppConstant.ID)[0]));
			persistAttributeValueAndCommonAttributeValue(product, multiParameters);
			return product.getId() + ";" + product.getProductName();
		}
		return AppConstant.FAIL;
	}
	
	// edit product event
	@RequestMapping(value = "/edit/{productId}", method = RequestMethod.GET)
	public ModelAndView listProducts(@PathVariable Long productId){
		// get all data
		List<BrtbProvince> provinces = provinceService.getAllProvinces();
		List<BrtbCategory> categories = categoryService.selectAll();
		List<BrtbAttribute> attributes = attributeService.getAllAttribute();
		List<Object[]> attributesOfProduct = productAttributeValueService.getAllAttributesOfAProduct(productId);
		List<ProductAttributeValueDTO> resultAttributes = combineAttribute(attributes, attributesOfProduct);
		List<BrtbBuilding> buildings = buildingService.getAllBuilding();
		
		List<BrtbCommonAttribute> commonAttributes = commonAttributeService.getAllCommonAttributes();
		List<Object[]> commonAttributesOfProduct = productCommonAttributeValueDao.getAllCommonAttributesOfAProduct(productId);
		List<ProductCommonAttributeValueDTO> resultCommonAttributes = combineCommonAttribute(commonAttributes, commonAttributesOfProduct);
		BrtbProduct product = productService.findProductById(productId);
		List<BrtbDistrict> districts = districtService.getDistrictByProvinceCode(product.getBrtbDistrict().getBrtbProvince().getProvinceCode());
		ModelAndView model = new ModelAndView("editProduct");
		if(provinces != null){
			model.addObject("provinces", provinces);
		}
		model.addObject("categories", categories);
		model.addObject("attributes", resultAttributes);
		model.addObject("districts", districts);
		model.addObject("interiors", initListInterior());
		model.addObject("commonAttributes", resultCommonAttributes);
		model.addObject("action", AppConstant.EDIT + " " + AppConstant.PRODUCT);
		model.addObject("buildings", buildings);
		model.addObject("product", product);
		return model;
	}

	private List<ProductAttributeValueDTO> combineAttribute(
			List<BrtbAttribute> attributes, List<Object[]> attributesOfProduct) {
		List<ProductAttributeValueDTO> dtos = new ArrayList<ProductAttributeValueDTO>();
		if(attributes != null & attributes.size() > 0){
			// create list dto => attribute + value
			for (BrtbAttribute brtbAttribute : attributes) {
				if(attributesOfProduct.size() > 0){
					if(!brtbAttribute.getAttributeCode().equals(AppConstant.PICTURE)){
						for(int i = 0; i< attributesOfProduct.size(); i++){
							if(brtbAttribute.getAttributeCode().equals(attributesOfProduct.get(i)[3])){
								ProductAttributeValueDTO dto = new ProductAttributeValueDTO(brtbAttribute.getAttributeCode(), brtbAttribute.getAttributeName(), (String)attributesOfProduct.get(i)[5], brtbAttribute.getRequired());
								dtos.add(dto);
								break;
							}
							if(i == attributesOfProduct.size() - 1){
								ProductAttributeValueDTO dto = new ProductAttributeValueDTO(brtbAttribute.getAttributeCode(), brtbAttribute.getAttributeName(), "", brtbAttribute.getRequired());
								dtos.add(dto);
							}
						}
					}
				}else{
					ProductAttributeValueDTO dto = new ProductAttributeValueDTO(brtbAttribute.getAttributeCode(), brtbAttribute.getAttributeName(), "", brtbAttribute.getRequired());
					dtos.add(dto);
				}
			}
		}
		movePictureAttributeToLastItemDTO(dtos);
		return dtos;
	}
	
	private void movePictureAttributeToLastItemDTO(List<ProductAttributeValueDTO> attributes) {
		ProductAttributeValueDTO pictureAttribute = null;
		ProductAttributeValueDTO specialAttribute = null;
		
		for (ProductAttributeValueDTO attribute : attributes) {
			if(attribute.getAttributeCode().equalsIgnoreCase(AppConstant.PICTURE)){
				pictureAttribute = attribute;
			}else if(attribute.getAttributeCode().equalsIgnoreCase(AppConstant.SPECIAL)){
				specialAttribute = attribute;
			}
		}
		
		if(pictureAttribute != null){
			attributes.remove(pictureAttribute);
			attributes.add(pictureAttribute);
		}
		
		if(specialAttribute != null){
			attributes.remove(specialAttribute);
			attributes.add(specialAttribute);
		}
	}
	
	private List<ProductCommonAttributeValueDTO> combineCommonAttribute(
			List<BrtbCommonAttribute> commonAttributes, List<Object[]> commonAttributesOfProduct) {
		List<ProductCommonAttributeValueDTO> dtos = new ArrayList<ProductCommonAttributeValueDTO>();
		if(commonAttributes != null & commonAttributes.size() > 0){
			// create list dto => attribute + value
			for (BrtbCommonAttribute attribute : commonAttributes) {
				if(commonAttributesOfProduct.size() > 0){
					for(int i = 0; i< commonAttributesOfProduct.size(); i++){
						if(attribute.getCommonAttributeCode().equals(commonAttributesOfProduct.get(i)[3])){
							ProductCommonAttributeValueDTO dto = new ProductCommonAttributeValueDTO(attribute.getCommonAttributeCode(), attribute.getCommonAttributeName(), Boolean.valueOf("true"));
							dtos.add(dto);
							break;
						}
						if(i == commonAttributesOfProduct.size() - 1){
							ProductCommonAttributeValueDTO dto = new ProductCommonAttributeValueDTO(attribute.getCommonAttributeCode(), attribute.getCommonAttributeName(), Boolean.valueOf("false"));
							dtos.add(dto);
						}
					}
				}else{
					ProductCommonAttributeValueDTO dto = new ProductCommonAttributeValueDTO(attribute.getCommonAttributeCode(), attribute.getCommonAttributeName(), Boolean.valueOf("false"));
					dtos.add(dto);
				}
			}
		}
		return dtos;
	}
	// ============================ create new product ==========================
	public BrtbProduct createProduct(Map<String, String[]> multiParameters){
		BrtbProduct product = productService.mergeProduct(initialProductInstance(multiParameters));
		persistAttributeValueAndCommonAttributeValue(product, multiParameters);
		return product;
	}
	
	private BrtbProduct initialProductInstance(Map<String, String[]> multiParameters) {
		BrtbProduct product = new BrtbProduct();
		setValueForProduct(product, multiParameters);
		return product;
	}
	
	// ==============================set value for product=======================
	public BrtbProduct setValueForProduct(BrtbProduct product, Map<String, String[]> multiParameters){
		String[] productNames = multiParameters.get(AppConstant.PRODUCTNAME);
		if(productNames != null ){
			product.setProductName(productNames[0]);
		}
		product.setMakeDate(new Date());
		product.setRecordStatus(RecordStatus.O);
		
		String[] categoryCodes = multiParameters.get(AppConstant.CATEGORY);
		if(categoryCodes != null){
			BrtbCategory category = categoryService.getCategoryByCode(categoryCodes[0]);
			product.setBrtbCategory(category);
		}
		String[] districtCodes = multiParameters.get(AppConstant.DISTRICT);
		if(districtCodes != null){
			BrtbDistrict district = districtService.getDistrictByCode(districtCodes[0]);
			product.setBrtbDistrict(district);
		}
		String[] building = multiParameters.get(AppConstant.BUILDING);
		if(building != null){
			if(!building[0].equals("--")){			
				BrtbBuilding brtbBuilding = buildingService.getBuildingByCode(building[0]);
				product.setBrtbBuilding(brtbBuilding);
			}else{
				product.setBrtbBuilding(null);
			}
		}
		String[] typeProduct = multiParameters.get(AppConstant.TYPEPRODUCT);
		if(typeProduct != null){
			product.setTypeProduct(typeProduct[0]);
		}
		String[] interior = multiParameters.get(AppConstant.INTERIOR);
		if(interior != null){
			BrtbInterior brtbInterior = interiorService.getInteriorCode(interior[0]);
			product.setBrtbInterior(brtbInterior);
		}
		String[] room = multiParameters.get(AppConstant.ROOM);
		if(room != null && !"".equals(room[0])){
			product.setRoom(Integer.parseInt(room[0]));
		}
		String[] bedroom = multiParameters.get(AppConstant.BEDROOM);
		if(bedroom != null && !"".equals(bedroom[0])){
			product.setBedroom(Integer.parseInt(bedroom[0]));
		}
		String[] bathroom = multiParameters.get(AppConstant.BATHROOM);
		if(bathroom != null && !"".equals(bathroom[0])){
			product.setBathroom(Integer.parseInt(bathroom[0]));
		}
		String[] price = multiParameters.get(AppConstant.PRICE);
		if(price != null && !"".equals(price[0])){
			product.setPrice(Integer.parseInt(price[0]));
		}
		String[] addressMap = multiParameters.get(AppConstant.ADDRESSMAP);
		if(addressMap != null){
			product.setAddressMap(addressMap[0]);
		}
		String[] address = multiParameters.get(AppConstant.ADDRESS);
		if(address != null){
			product.setAddress(address[0]);
		}
		String[] sqft = multiParameters.get(AppConstant.SQFT);
		if(sqft != null && !"".equals(sqft[0])){
			product.setSqft(Integer.parseInt(sqft[0]));
		}		
		String[] descriptions = multiParameters.get(AppConstant.DESCRIPTION);
		if(descriptions != null){
			product.setDescription(descriptions[0]);
		}
		String[] special = multiParameters.get(AppConstant.SPECIAL);
		if(special != null){
			product.setSpecial(special[0]);
		}
		// set current user login
		product.setMakerId(SecurityContextHolder.getContext().getAuthentication().getName());
		product.setUnitPrice(AppConstant.UNIT_PRICE);
		product.setUnitSqft(AppConstant.UNIT_SQFT);
		return product;
	}

	public void persistAttributeValueAndCommonAttributeValue(BrtbProduct product, Map<String, String[]> multiParameters){
		// remove all main product's attribute 
		for(String attribute : parameterString()){
			multiParameters.remove(multiParameters.get(attribute));
		}
		for(String value : multiParameters.keySet()) {
			if(value != null && multiParameters.get(value)[0].equals("true")){
				persisteCommonAttribute(product, value, multiParameters.get(value)[0]);
			}else if(value != null){
				// persist product attribute value
				persisteAttribute(product, value, multiParameters.get(value)[0]);
			}
		}
	}
	
	private List<BrtbInterior> initListInterior(){
		List<BrtbInterior> interiors = interiorService.getAllInterior();
		return interiors;
	}
	
	/**
	 * drop all attribute of a product with productId
	 */
	private void deleteAllAttributeProduct(Long productId){
		productAttributeValueService.deleteAllAttributeValueOfAProduct(productId);
	}
	
	/**
	 * drop all common attribute value of a product with productId
	 */
	private void deleteAllCommonAttributeValueProduct(Long productId){
		productCommonAttributeService.deleteAllCommonAttributeValueOfAProduct(productId);
	}
	
	/**
	 * persist common attribute
	 * @param product
	 * @param key common attribute code
	 * @param value always true
	 */
	private void persisteCommonAttribute(BrtbProduct product, String key, String value){
		BrtbCommonAttribute commonAttribute = commonAttributeService.getCommonAttributesByCode(key);
			// persist productcommon attrValue
		BrtbProductCommonAttrValue commonAttributeValue = new BrtbProductCommonAttrValue();
		commonAttributeValue.setMakeDate(new Date());
		commonAttributeValue.setRecordStatus(RecordStatus.O);
		commonAttributeValue.setValue(Boolean.valueOf("true"));
		commonAttributeValue.setBrtbProduct(product);
		commonAttributeValue.setBrtbCommonAttribute(commonAttribute);
		commonAttributeValue.setMakeDate(new Date());
		commonAttributeValue.setRecordStatus(RecordStatus.O);
		productCommonAttributeService.persist(commonAttributeValue);
	}
	
	/**
	 * persist attribute
	 * @param product
	 * @param key attribute code
	 * @param value
	 */
	private void persisteAttribute(BrtbProduct product, String key, String value){
		BrtbAttribute attribute = attributeService.getAttributeByCode(key);
		if(attribute != null){
			BrtbProductAttributeValue productAttributeValue = new BrtbProductAttributeValue();
			productAttributeValue.setBrtbProduct(product);
			productAttributeValue.setBrtbAttribute(attribute);
			productAttributeValue.setStrValue(value);
			productAttributeValue.setMakeDate(new Date());
			productAttributeValue.setRecordStatus(RecordStatus.O);
			productAttributeValueService.merge(productAttributeValue);
		}
	}
	
	// fixed product's attribute
	private List<String> parameterString(){
		List<String> strings = new ArrayList<String>();
		strings.add(AppConstant.PRODUCTCODE);
		strings.add(AppConstant.PRODUCTNAME);
		strings.add(AppConstant.CATEGORY);
		strings.add(AppConstant.BUILDING);
		strings.add(AppConstant.DISTRICT);
		strings.add(AppConstant.TYPEPRODUCT);
		strings.add(AppConstant.DESCRIPTION);
		strings.add(AppConstant.ROOM);
		strings.add(AppConstant.BEDROOM);
		strings.add(AppConstant.BATHROOM);
		strings.add(AppConstant.PRICE);
		strings.add(AppConstant.ADDRESSMAP);
		strings.add(AppConstant.ADDRESS);
		strings.add(AppConstant.SQFT);
		strings.add(AppConstant.INTERIOR);
		strings.add(AppConstant.ID);
		strings.add(AppConstant.SPECIAL);
		return strings;
	}
}
